/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { ArkFile, ts, AstTreeUtils } from "arkanalyzer";
import Logger, { LOG_MODULE_TYPE } from 'arkanalyzer/lib/utils/logger';
import { BaseChecker, BaseMetaData } from "../BaseChecker";
import { FileMatcher, MatcherCallback, MatcherTypes } from '../../Index';
import { Defects, IssueReport } from "../../model/Defects";
import { Rule } from "../../model/Rule";
import { RuleListUtil } from "../../utils/common/DefectsList";

const logger = Logger.getLogger(LOG_MODULE_TYPE.HOMECHECK, 'InitDeclarationsCheck');
const gMetaData: BaseMetaData = {
  severity: 1,
  ruleDocPath: "docs/init-declarations-check.md",
  description: "equire or disallow initialization in variable declarations.",
};

// 定义一个接口，用于存储问题的行列信息
interface LocationInfo {
  fileName: string;
  line: number;
  character: number;
}
export class InitDeclarationsCheck implements BaseChecker {
  readonly metaData: BaseMetaData = gMetaData;
  public rule: Rule;
  public defects: Defects[] = [];
  public issues: IssueReport[] = [];
  private defaultOptions = ["always"];

  private fileMatcher: FileMatcher = {
    matcherType: MatcherTypes.FILE,
  };

  public registerMatchers(): MatcherCallback[] {
    const fileMatcherCb: MatcherCallback = {
      matcher: this.fileMatcher,
      callback: this.check
    }
    return [fileMatcherCb];
  }

  public check = (arkFile: ArkFile) => {
    if (arkFile instanceof ArkFile) {
      const code = arkFile.getCode();
      if (!code) {
        return;
      }
      const filePath = arkFile.getFilePath();
      const asRoot = AstTreeUtils.getASTNode(arkFile.getName(), code);

      // 检查变量声明时进行初始化
      const LocationInfos = this.checkInitDeclarations(asRoot);

      // 输出结果
      LocationInfos.forEach(loc => {
        this.addIssueReportNode(loc.line, loc.character, filePath);
      });
    }
  }

  private checkInitDeclarations(sourceFile: ts.SourceFile): LocationInfo[] {
    const option = this.rule && this.rule.option[0] ? this.rule.option : this.defaultOptions;
    const locationInfos: LocationInfo[] = [];

    // 提取配置中的实际规则值
    const ruleConfig = option[0] ? option[0] : "always";

    const visit = (node: ts.Node) => {
      if (ts.isVariableDeclarationList(node)) {
        node.declarations.forEach((declarator) => {
          if (ruleConfig === 'always') {
            if (!declarator.initializer) {
              const { line, character } = sourceFile.getLineAndCharacterOfPosition(declarator.getStart());
              locationInfos.push({
                fileName: sourceFile.fileName,
                line: line + 1,
                character: character + 1,
              });
            }
          } else if (ruleConfig === 'never') {
            if (declarator.initializer) {
              // 检查是否为 const 声明
              const isConst = declarator.parent && ts.isVariableDeclarationList(declarator.parent) &&
                (declarator.parent.flags & ts.NodeFlags.Const) !== 0;
              if (!isConst) {
                // 提取配置中的ignoreForLoopInit
                const ruleConfigForLoop = option[1] ? option[1] : "";
                if (typeof ruleConfigForLoop === 'object' && ruleConfigForLoop !== null && 'ignoreForLoopInit' in ruleConfigForLoop) {
                  const ignoreForLoopInit = ruleConfigForLoop.ignoreForLoopInit;
                  // 检查是否为 for 循环的初始化部分
                  const isInForLoop = ts.isForStatement(declarator.parent?.parent) &&
                    declarator.parent?.parent.initializer === declarator.parent;
                  if (isInForLoop) {
                    if (!ignoreForLoopInit) {
                      const { line, character } = sourceFile.getLineAndCharacterOfPosition(declarator.getStart());
                      locationInfos.push({
                        fileName: sourceFile.fileName,
                        line: line + 1,
                        character: character + 1,
                      });
                    }
                  } else {
                    const { line, character } = sourceFile.getLineAndCharacterOfPosition(declarator.getStart());
                    locationInfos.push({
                      fileName: sourceFile.fileName,
                      line: line + 1,
                      character: character + 1,
                    });
                  }
                } else {
                  const { line, character } = sourceFile.getLineAndCharacterOfPosition(declarator.getStart());
                  locationInfos.push({
                    fileName: sourceFile.fileName,
                    line: line + 1,
                    character: character + 1,
                  });
                }
              }
            }
          }
        });
      }
      ts.forEachChild(node, visit);
    };

    visit(sourceFile);
    return locationInfos;
  }

  private addIssueReportNode(line: number, startCol: number, filePath: string) {
    const severity = this.rule.alert ?? this.metaData.severity;
    const fixKey = line + '%' + startCol + '%' + this.rule.ruleId;
    const mergeKey = filePath + '%' + fixKey + '%' + this.metaData.description;
    const defect = new Defects(line, startCol, this.metaData.description, severity, this.rule.ruleId,
      mergeKey, this.metaData.ruleDocPath, true, false, false, fixKey);
    this.issues.push(new IssueReport(defect, undefined));
    RuleListUtil.push(defect);
  }
}
